package com.karma.shared;

import java.util.HashMap;


/**
 * 
 * @author rajarshi
 *
 * The Board class manages the gameplay, it is initiated by the dimensions  grid (starting from 4x4).
 * This class can be used to make the game logic available across java supported platforms (currently used in GWT, Android etc.)
 * The view works with the instance of the class.  
 */

public class Board {
	
	private Pieces[][] pieces;	//	Each element of the color grid
	private int dim;			// Current dimension of the grid
	private Object[] keys;		// The number of colors used  
	
	private int currMoves;		// The current move number 
	private int par;			// the par associated with a level/dimension
	
	public static final int FALSE_MOVE = -2;	// status after each move - same color as intended by the user
	public static final int TRY_AGAIN = -1;		// status after each move - moves greater than par for a certain dimension
	public static final int END_LEVEL = 0;		// moves less than or equal to the par value 
	
	/**
	 * Construct the board with certain dimension and number of color set
	 * @param boardDimension
	 * @param keyColorsSet
	 */
	public Board(int boardDimension, Object[] keyColorsSet) {
		this.dim = boardDimension;
		this.par = par(boardDimension);
		this.keys = keyColorsSet;
		pieces = new Pieces[dim][dim];
		currMoves = 0;
		init();
	}

	/**
	 * Initialize the board with randomly filled colors
	 */
	private void init() {
		for (int i = 0; i < dim; i++){
			for (int j = 0; j < dim; j++){
				pieces[i][j] = new Pieces(keys[(int) (Math.random() * keys.length)]);
			}
		}
	}

	/**
	 * Update the board. Two step process floodfill the portion by newColor and update mutables (ref Pieces) for the same colors  
	 * @param oldKey the present color of the selected portion of the board
	 * @param newKey the color selected by the player
	 * @return the status whether false move, level up, unsuccessful or the current number of moves 
	 */
	public int updateMoves(Object oldKey, Object newKey){
		if (newKey.equals(oldKey)){
			return FALSE_MOVE;
		}
		
		++currMoves;
		
		floodFill(0, 0, oldKey, newKey);
		updateMutables(0, 0, newKey);
		
		if (levelComplete()){
			if(currMoves <= par){
				return END_LEVEL;
			} else {
				return TRY_AGAIN;
			}
		}
			
		return currMoves;	
	}
	
	/**
	 * Flood fill recursively
	 * @param posX
	 * @param posY
	 * @param oldKey
	 * @param newKey
	 */
	private void floodFill(int posX, int posY, Object oldKey, Object newKey){
		if (posX < 0 || posX >= dim || posY < 0 || posY >= dim){
			return;
		}
		if (pieces[posX][posY].colorType.equals(oldKey)){
			pieces[posX][posY].colorType = newKey;
			pieces[posX][posY].colorMutable = false;		
			floodFill(posX-1, posY, oldKey, newKey);
			floodFill(posX+1, posY, oldKey, newKey);
			floodFill(posX, posY-1, oldKey, newKey);
			floodFill(posX, posY+1, oldKey, newKey);
		}
		return;
	}
	
	/**
	 * Once we have with same color that piece becomes mutable.
	 * Mutable pieces used later to check whether game ended
	 * @param posX
	 * @param posY
	 * @param mutableColor
	 */
	private void updateMutables(int posX, int posY, Object mutableColor){
		if (posX < 0 || posX >= dim || posY < 0 || posY >= dim){
			return;
		}
		if (pieces[posX][posY].colorType.equals(mutableColor) && !pieces[posX][posY].colorMutable){
			pieces[posX][posY].colorMutable = true;
			updateMutables(posX-1, posY, mutableColor);
			updateMutables(posX+1, posY, mutableColor);
			updateMutables(posX, posY-1, mutableColor);
			updateMutables(posX, posY+1, mutableColor);
		}
		return;
	}
	
	/**
	 * If any piece is not changed by user's action then game is to go on, otherwise.
	 * @return true - if all pieces are mutables
	 * 			false - if any one piece is not mutable
	 */
	private boolean levelComplete() {
		for (int i = 0; i < dim; i++){
			for (int j = 0; j < dim; j++){
				if (!pieces[i][j].colorMutable){
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * Used by Views to get the color of a piece
	 * @param row
	 * @param col
	 * @return the object holding the color (implementation might be different for different views)
	 */
	public Object getColorforPos(int row, int col){
		return pieces[row][col].colorType;
	}
	
	/**
	 * Get the grid size of the board currently played
	 * @return
	 */
	public int getCurrDim(){
		return dim;
	}
	
	/**
	 * returns the par value
	 * @param boardDimension
	 * @return
	 */
	public static int par(int boardDimension) {
		
		return (boardDimension * 2  - ((int)Math.ceil((double)boardDimension / 10)));
		
		/*HashMap<Integer, Integer> hm = new HashMap<Integer, Integer>();
		for (int i = 4; i <= 40; i++){
			hm.put(i, i * 2  - ((int)Math.ceil((double)i / 10)));
		}
		
		return hm.get(boardDimension).intValue();*/
	}
}
